package com.excel.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.corpgovernment.common.apollo.BasicManageApollo;
import com.corpgovernment.common.base.BaseUserInfo;
import com.corpgovernment.organization.dataloader.BasicOssClientLoader;
import com.ctrip.corp.obt.generic.core.context.TenantContext;
import com.ctrip.corp.obt.generic.core.context.UserInfoContext;
import com.excel.entity.db.PubImpExcelJdLog;
import com.excel.enums.ExcelLogStatus;
import com.excel.model.ExcelImportConfig;
import com.excel.service.excel.BizImportResult;
import com.excel.service.TaskService;
import com.excel.service.IPubImpExcelJdLogService;
import com.excel.service.excel.ExcelImportProcessor;
import com.excel.service.excel.ExcelImportProcessorFactory;
import com.excel.util.FileUtil;
import com.excel.util.UUIDUtils;
import com.excel.util.MD5File;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * <p>
 *  导入任务实现类
 * </p>
 *
 *  @author hc
 * 
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class IITaskServiceImpl implements TaskService {

	@Autowired
	@Qualifier(value = "defaultThreadPoolExecutor")
	private ThreadPoolExecutor defaultThreadPoolExecutor;
	@Autowired
	private IPubImpExcelJdLogService pubImpExcelJdLogService;
	@Autowired
	private BasicOssClientLoader basicOssClientLoader;
	@Autowired
	private ExcelImportProcessorFactory factory;
	@Autowired
	private BasicManageApollo basicServerApollo;


	@Override
	public void createTask(MultipartFile file, String key) {
		LocalDateTime startTime = LocalDateTime.now();
		String originalFilename = file.getOriginalFilename();
		PubImpExcelJdLog pubImpExcelJdLog = new PubImpExcelJdLog();
		String fileMd5String;
		try {
			fileMd5String = MD5File.getFileMD5String(FileUtil.multipartFileToFile(file));
			// 上传excel资源到阿里云
			String fileUrl = basicOssClientLoader.putPrivateObject(file);

			createTask(key, startTime, originalFilename, pubImpExcelJdLog, fileMd5String, fileUrl);
		}catch (Exception e) {
			log.error("create task error {}",e.getMessage());
		}
		handleTask(pubImpExcelJdLog);
		/*defaultThreadPoolExecutor.execute(() -> {
			try {
				handleTask(pubImpExcelJdLog);
			} catch (Exception e) {
				e.printStackTrace();
			}
		});*/
	}

	@Override
	public <ERROR,DATA> void handleTask(PubImpExcelJdLog pubImpExcelJdLog) {

		// checkTaskStatus  如果有进行中的任务，则挂起当前任务，等待唤起，如果没有进行中的任务，则进行下一步

		ExcelImportProcessor processor = factory.getMessageHandler(pubImpExcelJdLog.getBizType());
		if (processor != null) {

			List<DATA> dataList = processor.assembleListFromExcel(pubImpExcelJdLog);

			BizImportResult<ERROR, DATA> checkExcelDataRes =  processor.checkExcelData(dataList);

			ExcelImportConfig excelImportConfig = processor.getConfig();

			if(CollectionUtil.isNotEmpty(checkExcelDataRes.getError()) && excelImportConfig.isFullRollBack()){
				// 有报错且全量回滚 任务结束
				// 更新任务状态
				fastFailTask(pubImpExcelJdLog);  // 快速失败
				// 记录错误日志
				processor.writeErrorLog(checkExcelDataRes.getError(),pubImpExcelJdLog);

				return;
			}

			// 更新任务状态
			runTask(pubImpExcelJdLog);

			// sendDelayMq
			processor.sendDelayCheckMq(pubImpExcelJdLog);

			if(excelImportConfig.getIsSharding()){
				// sendMq
				processor.sendMqWithCache(pubImpExcelJdLog, partition(dataList,excelImportConfig.getShardingNum()));

			}else {

				BizImportResult<ERROR, DATA> checkDbDataRes =  processor.checkDbData(dataList);

				if(CollectionUtil.isNotEmpty(checkDbDataRes.getError()) && excelImportConfig.isFullRollBack()){
					// 有报错且全量回滚 任务结束
					// 更新任务状态
					fastFailTask(pubImpExcelJdLog);  // 快速失败
					// 记录错误日志
					processor.writeErrorLog(checkDbDataRes.getError(),pubImpExcelJdLog);

					return;
				}

				BizImportResult<ERROR, DATA> checkDataPermissionRes =  processor.checkDataPermission(dataList);

				if(CollectionUtil.isNotEmpty(checkDataPermissionRes.getError()) && excelImportConfig.isFullRollBack()){
					// 有报错且全量回滚 任务结束
					// 更新任务状态
					fastFailTask(pubImpExcelJdLog);  // 快速失败
					// 记录错误日志
					processor.writeErrorLog(checkDbDataRes.getError(),pubImpExcelJdLog);

					return;
				}



			}

		}
	}


	@Override
	public void cancleTask(Long taskId) {

		// cancleTask

		// handleNextTask

	}


	private void createTask(String bizType, LocalDateTime startTime, String originalFilename, PubImpExcelJdLog pubImpExcelJdLog, String fileMd5String, String fileUrl) {
		pubImpExcelJdLog.setBizType(bizType);
		pubImpExcelJdLog.setPcNum(UUIDUtils.getUUID());
		pubImpExcelJdLog.setJdState(ExcelLogStatus.PENDING.name());
		pubImpExcelJdLog.setState(ExcelLogStatus.PENDING.getValue());
		pubImpExcelJdLog.setStartTime(startTime);
		pubImpExcelJdLog.setOperUid(0L);
		pubImpExcelJdLog.setSourceFileName(originalFilename);
		pubImpExcelJdLog.setSouceFilePath(fileUrl);
		pubImpExcelJdLog.setMd5code(fileMd5String);
		pubImpExcelJdLog.setTenantId(TenantContext.getTenantId());
		pubImpExcelJdLogService.insert(pubImpExcelJdLog);
	}


	private void completeTask(PubImpExcelJdLog pubImpExcelJdLog) {
		pubImpExcelJdLog.setState(ExcelLogStatus.SUCCESS.getValue());
		pubImpExcelJdLog.setJdState(ExcelLogStatus.SUCCESS.name());
		pubImpExcelJdLogService.updateState(pubImpExcelJdLog);
	}

	private void runTask(PubImpExcelJdLog pubImpExcelJdLog) {
		pubImpExcelJdLog.setState(ExcelLogStatus.PROCESSING.getValue());
		pubImpExcelJdLog.setJdState(ExcelLogStatus.PROCESSING.name());
		pubImpExcelJdLogService.updateState(pubImpExcelJdLog);
	}

	private void fastFailTask(PubImpExcelJdLog pubImpExcelJdLog) {
		pubImpExcelJdLog.setState(ExcelLogStatus.FAIL.getValue());
		pubImpExcelJdLog.setJdState(ExcelLogStatus.FAIL.name());
		pubImpExcelJdLogService.updateState(pubImpExcelJdLog);
	}






	public static <VIEW> List<List<VIEW>> partition(List<VIEW> list, int size) {
		if (list == null || list.isEmpty() || size < 1) {
			return Collections.emptyList();
		}
		int length = list.size();
		return IntStream.range(0, (length + size - 1) / size)
				.mapToObj(i -> list.subList(i * size, Math.min((i + 1) * size, length)))
				.collect(Collectors.toList());
	}


}
